use actix_web::{dev::Payload, error::{Error, ErrorInternalServerError}, FromRequest, HttpRequest};
use futures::future::{ready, Ready};
use std::collections::HashMap;

/// A structure to hold request headers.
#[derive(Clone, Debug)]
pub struct RequestHeaders {
    pub headers: HashMap<String, String>,
}

impl RequestHeaders {
    /// Try to create a RequestHeaders instance from an HttpRequest.
    pub fn try_from_request(req: &HttpRequest) -> Result<Self, Error> {
        let mut headers = HashMap::new();

        for (key, value) in req.headers().iter() {
            match value.to_str() {
                Ok(value_str) => {
                    headers.insert(key.as_str().to_string(), value_str.to_string());
                }
                Err(_) => {
                    return Err(ErrorInternalServerError(
                        format!("Invalid header value for key: {}", key.as_str()),
                    ));
                }
            }
        }

        Ok(RequestHeaders { headers })
    }

    /// Get the value of a header.
    pub fn get(&self, key: &str) -> Option<&String> {
        self.headers.get(key)
    }

    /// Check if a header exists.
    pub fn contains_key(&self, key: &str) -> bool {
        self.headers.contains_key(key)
    }

    /// Remove a header.
    pub fn remove(&mut self, key: &str) -> Option<String> {
        self.headers.remove(key)
    }

    /// Insert a new header.
    pub fn insert(&mut self, key: String, value: String) -> Option<String> {
        self.headers.insert(key, value)
    }

    /// List all headers.
    pub fn list(&self) -> &HashMap<String, String> {
        &self.headers
    }

    /// Print all headers in a formatted way.
    pub fn print(&self) {
        let formatted_headers: String = self.headers.iter()
            .map(|(key, value)| format!("  {}: {}", key, value))
            .collect::<Vec<String>>()
            .join("\n");
        log::info!("\nRequest Headers:\n{}", formatted_headers);
    }
}

impl FromRequest for RequestHeaders {
    type Error = Error;
    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
        match RequestHeaders::try_from_request(req) {
            Ok(headers) => ready(Ok(headers)),
            Err(err) => ready(Err(err)),
        }
    }
}
